package com.bluesky.plum.uimodels.standard;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.beanutils.PropertyUtils;

import com.bluesky.common.TreeNode;
import com.bluesky.plum.uimodels.standard.components.containers.window.Window;
import com.bluesky.plum.uimodels.standard.misc.Color;
import com.bluesky.plum.uimodels.standard.misc.Font;

/**
 * Any component is a container by default. But it should not always be a
 * container.
 * 
 * 
 * @author HZ00260
 * 
 */
public class UIComponent extends TreeNode {
	protected UIComponentBuilder uiComponentBuilder;

	/**
	 * it is unique
	 */
	protected String id;
	protected StandardUIComponentType componentType;
	protected String name;
	protected String caption;

	protected Font font;
	protected Color fontColor;
	protected Color backgroundColor;
	
	protected boolean visible;

	/**
	 * could be xml or html or any meaningful text that help the component to
	 * initialize itself.
	 */
	protected String template;

	/**
	 * after all the parameters needed during initialize is set, this method
	 * will be called. it is the last change before the component is delivered.
	 */
	public void initialize() {

		font=new Font();
		fontColor=Color.BLACK;
		backgroundColor=Color.WHITE;
	}

	public UIComponentBuilder getUiComponentBuilder() {
		return uiComponentBuilder;
	}

	public void setUiComponentBuilder(UIComponentBuilder uiComponentBuilder) {
		this.uiComponentBuilder = uiComponentBuilder;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getCaption() {
		return caption;
	}

	public void setCaption(String caption) {
		this.caption = caption;
	}

	public boolean isVisible() {
		return visible;
	}

	public void setVisible(boolean visible) {
		this.visible = visible;
	}

	public UIComponent() {

	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public StandardUIComponentType getComponentType() {
		return componentType;
	}

	public void setComponentType(StandardUIComponentType componentType) {
		this.componentType = componentType;
	}

	public String getTemplate() {
		return template;
	}

	public void setTemplate(String template) {
		this.template = template;
	}

	public Font getFont() {
		return font;
	}

	public void setFont(Font font) {
		this.font = font;
	}

	public Color getFontColor() {
		return fontColor;
	}

	public void setFontColor(Color fontColor) {
		this.fontColor = fontColor;
	}

	public Color getBackgroundColor() {
		return backgroundColor;
	}

	public void setBackgroundColor(Color backgroundColor) {
		this.backgroundColor = backgroundColor;
	}

	@Override
	public String toString() {

		return String.format("%-10s%-30s%-30s%-30s", isWindow(), this
				.getClass().getSimpleName(), name, caption);
	}

	/**
	 * it should be override, if it is a HTML component, then it should return a
	 * HTML tag. else if it is a swing component.
	 * 
	 * @return
	 */
	public Object getNativeComponent() {

		throw new RuntimeException("Standard component don't have native pair:"
				+ this);
		// return this;
	}

	public void addChildComponent(UIComponent uic) {
		addChild(uic);
	}

	public void addChildComponents(UIComponent[] uics) {
		for (UIComponent uic : uics)
			addChildComponent(uic);
	}

	public void removeChildComponent(UIComponent uic) {
		removeChild(uic);
	}

	public UIComponent getChildComponentByName(final String name) {
		UIComponent rslt = tranverse(new Find() {
			@Override
			public boolean find(UIComponent uic, int depth) {
				// System.out.println("in find()," + uic.name + "," + name);

				if (uic.name != null)
					return uic.name.equals(name);
				else
					return false;

			}
		}, 0);

		if (rslt != null)
			return rslt;
		else
			throw new RuntimeException(
					"getChildComponentByName not found name:" + name);
	}

	public List<UIComponent> getChildrenComponents() {
		List<UIComponent> rslt = new ArrayList<UIComponent>();
		for (TreeNode tn : getChildren()) {
			UIComponent uic = (UIComponent) tn;

			rslt.add(uic);
		}
		return rslt;
	}

	static interface Find {
		boolean find(UIComponent uic, int depth);
	}

	public UIComponent getChildComponentById(final String id) {
		UIComponent rslt = tranverse(new Find() {
			@Override
			public boolean find(UIComponent uic, int depth) {
				/*
				 * System.out.println("");
				 * 
				 * for(int i=0;i<depth;i++) System.out.print("  ");
				 * 
				 * System.out.print("in find()," + uic.id);
				 */

				if (uic.id != null)
					return uic.id.equals(id);
				else
					return false;

			}
		}, 0);

		if (rslt != null)
			return rslt;
		else
			throw new RuntimeException("getChildComponentById not found id:"
					+ id + " under component:" + this.id);
	}

	protected UIComponent tranverse(Find find, int depth) {
		if (find.find(this, depth))
			return this;

		for (TreeNode tn : getChildren()) {
			UIComponent uic = (UIComponent) tn;
			UIComponent rslt = uic.tranverse(find, depth + 1);
			if (rslt != null)
				return rslt;
		}

		return null;
	}

	/*
	 * copy field values back to POJO properties.
	 */
	public void writePojo(Object obj) {

		Field[] fields = obj.getClass().getDeclaredFields();
		for (Field f : fields) {

			try {
				FieldUIComponent uic = (FieldUIComponent) getChildComponentByName(f
						.getName());
				PropertyUtils.setProperty(obj, f.getName(), uic
						.getDomainFieldValue());

				// f.set(obj, uic.getDomainFieldValue());

			} catch (Exception e) {
				throw new RuntimeException(e + "field name is " + f.getName());
			}
		}

	}

	public void readPojo(Object obj) {

		Field[] fields = obj.getClass().getDeclaredFields();
		for (Field f : fields) {

			try {
				FieldUIComponent uic = (FieldUIComponent) getChildComponentByName(f
						.getName());

				Object value = PropertyUtils.getProperty(obj, f.getName());
				uic.setDomainFieldValue(value);

			} catch (Exception e) {

			}
		}
	}

	public boolean isWindow() {
		return (Window.class.isInstance(this));

	}

	/**
	 * original idea is to make a "getWindow()", but, I don't want to let this
	 * class depend on the Window class.
	 * 
	 * @param clazz
	 * @return
	 */
	protected UIComponent getComponentByClass(Class clazz) {
		System.out.println("getComponentByClass()" + this);
		if (clazz.isInstance(this)) {
			System.out.println("getComponentByClass() -- Found");
			return this;
		} else {
			System.out.println("getComponentByClass() -- Keep looking for");
			UIComponent p = (UIComponent) getParent();
			if (p == null) {
				System.out
						.println("getComponentByClass() -- not found until parent null");
				return null;
			} else {
				System.out
						.println("getComponentByClass() -- try to find its parent");
				return p.getComponentByClass(clazz);
			}
		}
	}

	public Window getWindow() {

		UIComponent uic = getComponentByClass(Window.class);
		// System.out.println("getComponentByClass() ++Found");

		if (uic != null)
			return (Window) uic;
		else {

			throw new RuntimeException("the root node is not a Window.");
		}
	}
}
